/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.gallery3d.ui;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
//import android.support.v4.print.PrintHelper;
import android.view.Menu;
import android.view.MenuItem;

import com.android.gallery3d.R;
import com.android.gallery3d.app.AbstractGalleryActivity;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.MediaItem;
import com.android.gallery3d.data.MediaObject;
import com.android.gallery3d.data.Path;
import com.android.gallery3d.filtershow.crop.CropActivity;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.GalleryUtils;
import com.android.gallery3d.util.ThreadPool.Job;
import com.android.gallery3d.util.ThreadPool.JobContext;

import java.util.ArrayList;

/**
 * ActionBar Menu Executor
 * 
 * @author lewa
 *
 */
public class MenuExecutor {
	private static final String TAG = MenuExecutor.class.getSimpleName();

	private static final int MSG_TASK_COMPLETE = 1;
	private static final int MSG_TASK_UPDATE = 2;
	private static final int MSG_TASK_START = 3;
	private static final int MSG_DO_SHARE = 4;

	public static final int EXECUTION_RESULT_SUCCESS = 1;
	public static final int EXECUTION_RESULT_FAIL = 2;
	public static final int EXECUTION_RESULT_CANCEL = 3;

	private ProgressDialog mDialog;
	private Future<?> mTask;
	// wait the operation to finish when we want to stop it.
	private boolean mWaitOnStop;
	private boolean mPaused;

	private final AbstractGalleryActivity mActivity;
	private final SelectionManager mSelectionManager;
	private final Handler mHandler;

	private static ProgressDialog createProgressDialog(Context context,
			int titleId, int progressMax) {
		ProgressDialog dialog = new ProgressDialog(context);
		dialog.setTitle(titleId);
		dialog.setMax(progressMax);
		dialog.setCancelable(false);
		dialog.setIndeterminate(false);
		if (progressMax > 1) {
			dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		}
		return dialog;
	}

	public interface ProgressListener {
		public void onConfirmDialogShown();

		public void onConfirmDialogDismissed(boolean confirmed);

		public void onProgressStart();

		public void onProgressUpdate(int index);

		public void onProgressComplete(int result);
	}

	public MenuExecutor(AbstractGalleryActivity activity,
			SelectionManager selectionManager) {
		mActivity = Utils.checkNotNull(activity);
		mSelectionManager = Utils.checkNotNull(selectionManager);
		mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
			@Override
			public void handleMessage(Message message) {
				switch (message.what) {
				case MSG_TASK_START: {
					if (message.obj != null) {
						ProgressListener listener = (ProgressListener) message.obj;
						listener.onProgressStart();
					}
					break;
				}
				case MSG_TASK_COMPLETE: {
					stopTaskAndDismissDialog();
					if (message.obj != null) {
						ProgressListener listener = (ProgressListener) message.obj;
						listener.onProgressComplete(message.arg1);
					}
					mSelectionManager.leaveSelectionMode();
					break;
				}
				case MSG_TASK_UPDATE: {
					if (mDialog != null && !mPaused)
						mDialog.setProgress(message.arg1);
					if (message.obj != null) {
						ProgressListener listener = (ProgressListener) message.obj;
						listener.onProgressUpdate(message.arg1);
					}
					break;
				}
				case MSG_DO_SHARE: {
					((Activity) mActivity).startActivity((Intent) message.obj);
					break;
				}
				}
			}
		};
	}

	private void stopTaskAndDismissDialog() {
		if (mTask != null) {
			if (!mWaitOnStop)
				mTask.cancel();
			if (mDialog != null && mDialog.isShowing())
				mDialog.dismiss();
			mDialog = null;
			mTask = null;
		}
	}

	public void resume() {
		mPaused = false;
		if (mDialog != null)
			mDialog.show();
	}

	public void pause() {
		mPaused = true;
		if (mDialog != null && mDialog.isShowing())
			mDialog.hide();
	}

	public void destroy() {
		stopTaskAndDismissDialog();
	}

	private void onProgressUpdate(int index, ProgressListener listener) {
		mHandler.sendMessage(mHandler.obtainMessage(MSG_TASK_UPDATE, index, 0,
				listener));
	}

	private void onProgressStart(ProgressListener listener) {
		mHandler.sendMessage(mHandler.obtainMessage(MSG_TASK_START, listener));
	}

	private void onProgressComplete(int result, ProgressListener listener) {
		mHandler.sendMessage(mHandler.obtainMessage(MSG_TASK_COMPLETE, result,
				0, listener));
	}

	public static void updateMenuOperation(Menu menu, int supported) {
		boolean supportDelete = (supported & MediaObject.SUPPORT_DELETE) != 0;
		boolean supportRotate = (supported & MediaObject.SUPPORT_ROTATE) != 0;
		boolean supportCrop = (supported & MediaObject.SUPPORT_CROP) != 0;
		boolean supportTrim = (supported & MediaObject.SUPPORT_TRIM) != 0;
		boolean supportMute = (supported & MediaObject.SUPPORT_MUTE) != 0;
		boolean supportShare = (supported & MediaObject.SUPPORT_SHARE) != 0;
		boolean supportSetAs = (supported & MediaObject.SUPPORT_SETAS) != 0;
		boolean supportShowOnMap = (supported & MediaObject.SUPPORT_SHOW_ON_MAP) != 0;
		boolean supportCache = (supported & MediaObject.SUPPORT_CACHE) != 0;
		boolean supportEdit = (supported & MediaObject.SUPPORT_EDIT) != 0;
		boolean supportInfo = (supported & MediaObject.SUPPORT_INFO) != 0;
		boolean supportPrint = (supported & MediaObject.SUPPORT_PRINT) != 0;
		// supportPrint &= PrintHelper.systemSupportsPrint();

		setMenuItemVisible(menu, R.id.action_delete, supportDelete);
		setMenuItemVisible(menu, R.id.action_rotate_ccw, supportRotate);
		setMenuItemVisible(menu, R.id.action_rotate_cw, supportRotate);
		setMenuItemVisible(menu, R.id.action_crop, supportCrop);
		setMenuItemVisible(menu, R.id.action_trim, supportTrim);
		setMenuItemVisible(menu, R.id.action_mute, supportMute);
		// Hide panorama until call to updateMenuForPanorama corrects it
		setMenuItemVisible(menu, R.id.action_share_panorama, false);
		setMenuItemVisible(menu, R.id.action_share, supportShare);
		setMenuItemVisible(menu, R.id.action_setas, supportSetAs);
		setMenuItemVisible(menu, R.id.action_show_on_map, supportShowOnMap);
		setMenuItemVisible(menu, R.id.action_edit, supportEdit);
		// setMenuItemVisible(menu, R.id.action_simple_edit, supportEdit);
		setMenuItemVisible(menu, R.id.action_details, supportInfo);
		setMenuItemVisible(menu, R.id.print, supportPrint);
	}

	public static void updateMenuForPanorama(Menu menu,
			boolean shareAsPanorama360, boolean disablePanorama360Options) {
		setMenuItemVisible(menu, R.id.action_share_panorama, shareAsPanorama360);
		if (disablePanorama360Options) {
			setMenuItemVisible(menu, R.id.action_rotate_ccw, false);
			setMenuItemVisible(menu, R.id.action_rotate_cw, false);
		}
	}

	private static void setMenuItemVisible(Menu menu, int itemId,
			boolean visible) {
		MenuItem item = menu.findItem(itemId);
		if (item != null)
			item.setVisible(visible);
	}

	private Path getSingleSelectedPath() {
		ArrayList<Path> ids = mSelectionManager.getSelected(true);
		Utils.assertTrue(ids.size() == 1);
		return ids.get(0);
	}

	private Intent getIntentBySingleSelectedPath(String action) {
		DataManager manager = mActivity.getDataManager();
		Path path = getSingleSelectedPath();
		String mimeType = getMimeType(manager.getMediaType(path));
		return new Intent(action).setDataAndType(manager.getContentUri(path),
				mimeType);
	}

	private void onMenuClicked(int action, ProgressListener listener) {
		onMenuClicked(action, listener, false, true);
	}

	public void onMenuClicked(int action, ProgressListener listener,
			boolean waitOnStop, boolean showDialog) {
		int title;
		switch (action) {
		case R.id.action_select_all:
			Log.d(TAG, "Action select all !");
			if (mSelectionManager.inSelectAllMode()) {
				mSelectionManager.deSelectAll();
			} else {
				mSelectionManager.selectAll();
			}
			return;
		case R.id.action_crop: {
			Log.d(TAG, "Action crop !");
			Intent intent = getIntentBySingleSelectedPath(CropActivity.CROP_ACTION);
			((Activity) mActivity).startActivity(intent);
			return;
		}
		case R.id.action_edit: {
			Log.d(TAG, "Action edit !");
			Intent intent = getIntentBySingleSelectedPath(Intent.ACTION_EDIT)
					.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
			((Activity) mActivity).startActivity(Intent.createChooser(intent,
					null));
			return;
		}
		case R.id.action_setas: {
			Log.d(TAG, "Action setas !");
			Intent intent = getIntentBySingleSelectedPath(
					Intent.ACTION_ATTACH_DATA).addFlags(
					Intent.FLAG_GRANT_READ_URI_PERMISSION);
			intent.putExtra("mimeType", intent.getType());
			Activity activity = mActivity;
			activity.startActivity(Intent.createChooser(intent,
					activity.getString(R.string.set_as)));
			return;
		}
		case R.id.action_delete:
			Log.d(TAG, "Action delete !");
			title = R.string.delete;
			break;
		case R.id.action_rotate_cw:
			title = R.string.rotate_right;
			break;
		case R.id.action_rotate_ccw:
			title = R.string.rotate_left;
			break;
		case R.id.action_show_on_map:
			title = R.string.show_on_map;
			break;
		default:
			return;
		}
		startAction(action, title, listener, waitOnStop, showDialog);
	}

	private class ConfirmDialogListener implements OnClickListener,
			OnCancelListener {
		private final int mActionId;
		private final ProgressListener mListener;

		public ConfirmDialogListener(int actionId, ProgressListener listener) {
			mActionId = actionId;
			mListener = listener;
		}

		@Override
		public void onClick(DialogInterface dialog, int which) {
			if (which == DialogInterface.BUTTON_POSITIVE) {
				if (mListener != null) {
					mListener.onConfirmDialogDismissed(true);
				}
				onMenuClicked(mActionId, mListener);
			} else {
				if (mListener != null) {
					mListener.onConfirmDialogDismissed(false);
				}
			}
		}

		@Override
		public void onCancel(DialogInterface dialog) {
			if (mListener != null) {
				mListener.onConfirmDialogDismissed(false);
			}
		}
	}

	public void onMenuClicked(MenuItem menuItem, String confirmMsg,
			final ProgressListener listener) {
		final int action = menuItem.getItemId();

		if (confirmMsg != null) {
			if (listener != null)
				listener.onConfirmDialogShown();
			ConfirmDialogListener cdl = new ConfirmDialogListener(action,
					listener);
			new AlertDialog.Builder(mActivity.getAndroidContext())
					.setMessage(confirmMsg).setOnCancelListener(cdl)
					.setPositiveButton(R.string.ok, cdl)
					.setNegativeButton(R.string.cancel, cdl).create().show();
		} else {
			onMenuClicked(action, listener);
		}
	}

	public void startAction(int action, int title, ProgressListener listener) {
		startAction(action, title, listener, false, true);
	}

	public void startAction(int action, int title, ProgressListener listener,
			boolean waitOnStop, boolean showDialog) {
		ArrayList<Path> ids = mSelectionManager.getSelected(false);
		stopTaskAndDismissDialog();

		Activity activity = mActivity;
		if (showDialog) {
			mDialog = createProgressDialog(activity, title, ids.size());
			mDialog.show();
		} else {
			mDialog = null;
		}
		MediaOperation operation = new MediaOperation(action, ids, listener);
		mTask = mActivity.getBatchServiceThreadPoolIfAvailable().submit(
				operation, null);
		mWaitOnStop = waitOnStop;
	}

	public void startSingleItemAction(int action, Path targetPath) {
		ArrayList<Path> ids = new ArrayList<Path>(1);
		ids.add(targetPath);
		mDialog = null;
		MediaOperation operation = new MediaOperation(action, ids, null);
		mTask = mActivity.getBatchServiceThreadPoolIfAvailable().submit(
				operation, null);
		mWaitOnStop = false;
	}

	public static String getMimeType(int type) {
		switch (type) {
		case MediaObject.MEDIA_TYPE_IMAGE:
			return GalleryUtils.MIME_TYPE_IMAGE;
		case MediaObject.MEDIA_TYPE_VIDEO:
			return GalleryUtils.MIME_TYPE_VIDEO;
		default:
			return GalleryUtils.MIME_TYPE_ALL;
		}
	}

	private boolean execute(DataManager manager, JobContext jc, int cmd,
			Path path) {
		boolean result = true;
		Log.v(TAG, "Execute cmd: " + cmd + " for " + path);
		long startTime = System.currentTimeMillis();

		switch (cmd) {
		case R.id.action_delete:
			manager.delete(path);
			break;
		case R.id.action_rotate_cw:
			manager.rotate(path, 90);
			break;
		case R.id.action_rotate_ccw:
			manager.rotate(path, -90);
			break;
		case R.id.action_toggle_full_caching: {
			MediaObject obj = manager.getMediaObject(path);
			int cacheFlag = obj.getCacheFlag();
			if (cacheFlag == MediaObject.CACHE_FLAG_FULL) {
				cacheFlag = MediaObject.CACHE_FLAG_SCREENNAIL;
			} else {
				cacheFlag = MediaObject.CACHE_FLAG_FULL;
			}
			obj.cache(cacheFlag);
			break;
		}
		case R.id.action_show_on_map: {
			MediaItem item = (MediaItem) manager.getMediaObject(path);
			double latlng[] = new double[2];
			item.getLatLong(latlng);
			if (GalleryUtils.isValidLocation(latlng[0], latlng[1])) {
				GalleryUtils.showOnMap(mActivity, latlng[0], latlng[1]);
			}
			break;
		}
		default:
			throw new AssertionError();
		}
		Log.v(TAG, "It takes " + (System.currentTimeMillis() - startTime)
				+ " ms to execute cmd for " + path);
		return result;
	}

	private class MediaOperation implements Job<Void> {
		private final ArrayList<Path> mItems;
		private final int mOperation;
		private final ProgressListener mListener;

		public MediaOperation(int operation, ArrayList<Path> items,
				ProgressListener listener) {
			mOperation = operation;
			mItems = items;
			mListener = listener;
		}

		@Override
		public Void run(JobContext jc) {
			int index = 0;
			DataManager manager = mActivity.getDataManager();
			int result = EXECUTION_RESULT_SUCCESS;
			try {
				onProgressStart(mListener);
				for (Path id : mItems) {
					if (jc.isCancelled()) {
						result = EXECUTION_RESULT_CANCEL;
						break;
					}
					if (!execute(manager, jc, mOperation, id)) {
						result = EXECUTION_RESULT_FAIL;
					}
					onProgressUpdate(index++, mListener);
				}
			} catch (Throwable th) {
				Log.e(TAG, "failed to execute operation " + mOperation + " : "
						+ th);
			} finally {
				onProgressComplete(result, mListener);
			}
			return null;
		}
	}
}
